package com.blogspot.javagamexyz.gamexyz.abilities;

import com.artemis.Entity;
import com.artemis.utils.Bag;
import com.artemis.utils.ImmutableBag;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;
import com.blogspot.javagamexyz.gamexyz.components.Damage;
import com.blogspot.javagamexyz.gamexyz.components.Stats;
import com.blogspot.javagamexyz.gamexyz.custom.Globals;
import com.blogspot.javagamexyz.gamexyz.custom.Pair;
import com.blogspot.javagamexyz.gamexyz.custom.Dice;
import com.blogspot.javagamexyz.gamexyz.maps.MapTools;

public class Action2 {
	
	
	//new bastard
	public Action2(String name, String statType, int mpCost, String tnType, int range, int field, Dice dice, int successMod, int dmgMod,
			ActionProcessor actionProcessor,
			ScoreCalculator scoreCalculator,
			FieldCalculator fieldCalculator,
			RangeCalculator rangeCalculator) {
		this.name = name;
		this.statType = statType;	//the stat which is rolled to see if the ability hits
		this.tnType = tnType;		//the target number required 'to hit'. usually based on enemy entity's stat
		this.mpCost = mpCost;		//for abilities requiring power points
		this.range = range;			//for ranged abilities
		this.field = field;			//for when the ability can hit multiple targets
		this.dice = dice;			//dice values for damage caused by the attack
		this.successMod = successMod;	//for when the ability has innate 'chance to hit' modifier independent of the entity's stats
		this.dmgMod = dmgMod;			//for when the ability has innate damage modifier independent of entity's stats and weapon dmg
		this.actionProcessor = actionProcessor;
		this.scoreCalculator = scoreCalculator;
		this.fieldCalculator = fieldCalculator;
		this.rangeCalculator = rangeCalculator;
	}

	//TODO er this is messy so we will probably get rid of it
	//but we can keep it as a generic action, but we'll come back to it after creating the others in ACTIONFACTORY
	/*public Action2(String name, String statType, int mpCost, String tnType, int range, int field, Dice dice)	{
		this.name = name;
		this.mpCost = mpCost;
		this.range = range;
		this.field = field;
		this.statType = statType;
		this.tnType = tnType;
		
		//int tn = 4;
		/*if (tnType.equals(Globals.DEFAULT)){
			tn = 4;
		}/
		
		actionProcessor = new ActionProcessor(){

			@Override
			public void process(Entity sourceE, Array<Entity> targets, Action2 action) {
				Stats source = sourceE.getComponent(Stats.class);
				source.magic -= action.mpCost;
				boolean hitOnce = false;			
				int tn;
				int damage ;			
				int hitRaise;
				int statRoll;
				
				for (Entity targetE : targets){ //this is for when there are multiple targets for an attack...
					Stats target = targetE.getComponent(Stats.class);
					damage = 0;
					hitRaise = 0;
					statRoll = 0;
					
					//Get the Target Number
					if (action.tnType.equals(Globals.DEFAULT)){
						tn = 4;
					}
					else{
						tn = target.getStatTotal(action.tnType);
					}
					
					//Roll to determine hit and damage
					statRoll = source.getStatRoll(action.statType);
					if (statRoll >= tn) { //HIT
						if (!hitOnce) {
							source.xp += 10;
							hitOnce = true;
						}
						//damage = (int)(MathUtils.random(0.8f,1.2f)*(action.strength + source.getStrength() - target.getToughness()));//TODO fix that
						
						//determine if wounded or just shaken***************************************
						if (statRoll - tn >= 4){
							hitRaise = 1;
						}
						
						
						
						//damage roll (with raises)
						//well shit
						//For melee attacks, we need to get StrengthRoll + WeaponRoll + RaiseD6 (weapon is 0 for unarmed) {{NO WILD}}
						//for ranged attacks, we just get WeaponRoll + RaiseD6
						//for this to work at all we need items and the dice roller
						
						//so either we have an inventory system
						//or stats will have an equipped weapon(s)
						//but sometimes you may have more than 1 weapon
						
						
						target.applyDamage(damage); // 
						//damage = hitRaise;
						//if (damage < 1) damage = 1;
					}

					targetE.addComponent(new Damage(damage));
					targetE.changedInWorld();
								
					
				}
			}
			
		};
	}*/
	
	
	
	
	
	//TODO get rid of this shit////////////////////////////////////////////////////////////////////////////////
	//old bastard
	public Action2(String name, int strength, int mpCost, int baseProbability, int range, int field,
			ActionProcessor actionProcessor,
			ScoreCalculator scoreCalculator,
			FieldCalculator fieldCalculator,
			RangeCalculator rangeCalculator) {
		this.name = name;
		this.strength = strength;
		this.baseProbability = baseProbability;
		this.mpCost = mpCost;
		this.range = range;
		this.field = field;
		this.actionProcessor = actionProcessor;
		this.scoreCalculator = scoreCalculator;
		this.fieldCalculator = fieldCalculator;
		this.rangeCalculator = rangeCalculator;
	}
	//the old action2 - builds default instance based on mpCost I guess
	public Action2(String name, int strength, int mpCost, int baseProbability, int range, int field) {
		this.name = name;
		this.mpCost = mpCost;
		this.range = range;
		this.field = field;
		this.strength = strength;
		this.baseProbability = baseProbability;
		
		actionProcessor = new ActionProcessor() {
			@Override
			public void process(Entity sourceE, Array<Entity> targets, Action2 action) {
				Stats source = sourceE.getComponent(Stats.class);
				source.magic -= action.mpCost;
				boolean hitOnce = false;
				
				//TODO sort all this out
				//this handles all actions against a character, whether helpful or painful
				//remember 4 points over a TN is a raise and may need to be tracked for extra dmg
				//also snake eyes will be important
				//we're going to ignore fate chips for now
				for (Entity targetE : targets) {	//this is for when there are multiple targets for an attack...
					Stats target = targetE.getComponent(Stats.class);
					int damage = 0;
					
					//int probability = action.baseProbability; // Target Number should go here
					//int tn = target.getParry();	//need to determine what sort of TN we need here... arg
					int tn = 4;
					
					//if (MathUtils.random(100) < probability) { //HIT	//dice rolling should go here
					if (MathUtils.random(100) < tn) { //HIT
						if (!hitOnce) {
							source.xp += 10;
							hitOnce = true;
						}
						//damage = (int)(MathUtils.random(0.8f,1.2f)*(action.strength + source.getStrength() - target.getToughness()));//TODO fix that
						damage = 1;
						if (damage < 1) damage = 1;
					}

					targetE.addComponent(new Damage(damage));
					targetE.changedInWorld();
				}
			}
		};
		
		scoreCalculator = new ScoreCalculator() {
			@Override
			public ImmutableBag<Float> calculateScore(Stats source, ImmutableBag<Stats> targets, Action2 action) {				
				// If we can't even cast it, then don't bother
				int MP = source.magic;
				if (action.mpCost > MP) return null;
				
				Bag<Float> scoreBag = new Bag<Float>();
				
				// Get the cost to the source
				scoreBag.add(0.1f*(float)action.mpCost / (float)MP);
				
				// Get the scores for each target
				for (int i = 0; i < targets.size(); i++) {
					Stats target = targets.get(i);
					//int HP = target.health;	//TODO what to do about this?
					int HP = target.wounds;	//error suppression for now,  need to work out scoring system
					//int damage = (int)(MathUtils.random(0.8f,1.2f)*(action.strength + source.getStrength() - target.getToughness()));
					int damage = 1;
					if (damage < 1) damage = 1;
					scoreBag.add((float)action.baseProbability/100f * (float)Math.min(damage, HP) / (float)HP);
				}
				
				return scoreBag;
			}
		};
		
		fieldCalculator = new FieldCalculator() {
			@Override
			public Array<Pair> getField(Pair target, Action2 action) {
				Array<Pair> field = MapTools.getNeighbors(target.x, target.y, action.field-1);	//-1 won't mess things up here?
				field.add(target);
				return field;
			}
		};
		
		rangeCalculator = new RangeCalculator() {
			@Override
			public Array<Pair> getRange(Pair source, Action2 action) {
				return MapTools.getNeighbors(source.x, source.y, action.range);
			}
		};
	}
	
	//TODO fix this up too
	public int mpCost, range, field, baseProbability, strength;	//TODO remove baseProb
	public int stat, stat_mods, targetNumber, successMod, dmgMod;
	public String tnType, statType;
	public Dice dice;
	
	public String name, description;
	
	public ActionProcessor actionProcessor;
	public ScoreCalculator scoreCalculator;
	public FieldCalculator fieldCalculator;
	public RangeCalculator rangeCalculator;
	
	public interface ActionProcessor {
		public void process(Entity source, Array<Entity> targets, Action2 action);
	}
	
	public interface ScoreCalculator {
		public ImmutableBag<Float> calculateScore(Stats source, ImmutableBag<Stats> target, Action2 action);
	}
	
	public interface FieldCalculator {
		public Array<Pair> getField(Pair target, Action2 action);
	}
	
	public interface RangeCalculator {
		public Array<Pair> getRange(Pair source, Action2 action);
	}
}
